/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.common;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.inspur.edp.lcm.metadata.api.IMdExtRuleContent;
import com.inspur.edp.lcm.metadata.api.IMetadataContent;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.common.configuration.MdExtRuleSerializerHelper;
import com.inspur.edp.lcm.metadata.common.configuration.MetadataSerializerHelper;
import com.inspur.edp.lcm.metadata.spi.MdExtendRuleSerializer;
import com.inspur.edp.lcm.metadata.spi.MetadataContentSerializer;
import io.iec.edp.caf.caching.api.RedisDataSerializer;
import lombok.var;

import java.io.IOException;

/**
 * 元数据对象的序列化
 */
public class MetadataSerializer implements RedisDataSerializer {

    private ObjectMapper mapper = ServiceUtils.getMapper();

    @Override
    public <T> T deserialize(String fileString, Class<T> clazz) {
        if (fileString == null || fileString.isEmpty()) {
            return null;
        }
        try {
            IMetadataContent metadataContent;
            JsonNode metadataObj = mapper.readTree(fileString);
            GspMetadata metadata = buildMetadataBaseInfo(metadataObj);

            //IMetdataContent需要调用各元数据反序列化
            MetadataContentSerializer manager = MetadataSerializerHelper.getInstance().getManager(metadata.getHeader().getType());
            if (manager == null) {
                throw new RuntimeException("未能正常获取元数据序列化器，请检查配置,元数据类型为：" + metadata.getHeader().getType());
            }
            JsonNode content = metadataObj.findValue(MetadataPropertyUtils.content);
            metadataContent = content.isTextual() ? manager.DeSerialize(mapper.readTree(handleJsonString(content.toString()))) : manager.DeSerialize(content);
            if (metadataContent == null) {
                throw new RuntimeException("反序列化的content为null：" + fileString);
            }
            metadata.setContent(metadataContent);
            JsonNode extRule = metadataObj.findValue(MetadataPropertyUtils.extendRule);
            if (extRule != null) {
                MdExtendRuleSerializer mdExtRulemanager = MdExtRuleSerializerHelper.getInstance().getManager(metadata.getHeader().getType());
                if (mdExtRulemanager != null) {
                    JsonNode handledContent = mapper.readTree(extRule.toString());
                    IMdExtRuleContent extRuleContent = mdExtRulemanager.deSerialize(handledContent);
                    metadata.setExtendRule(extRuleContent);
                }
            }

            return (T) metadata;
        } catch (RuntimeException | IOException e) {
            throw new RuntimeException("反序列化元数据失败", e);
        }
    }

    /**
     * JSON转换为元数据对象
     *
     * @param metadataObj
     * @return
     */
    public GspMetadata buildMetadataBaseInfo(JsonNode metadataObj) {
        GspMetadata metadata;
        try {
            String headerStr = metadataObj.findValue(MetadataPropertyUtils.header).toString();
            var refNode = metadataObj.findValue(MetadataPropertyUtils.refs);
            String constraintsStr;
            if (refNode == null) {
                constraintsStr = "[]";
            } else {
                constraintsStr = refNode.toString();
            }

            var extendPropertyNode = metadataObj.findValue(MetadataPropertyUtils.extendProperty);
            var extendedNode = metadataObj.findValue(MetadataPropertyUtils.extended);
            var properties = metadataObj.findValue(MetadataPropertyUtils.properties);
            var version = metadataObj.findValue(MetadataPropertyUtils.version);
            var previousVersion = metadataObj.findValue(MetadataPropertyUtils.previousVersion);
            var relativePath = metadataObj.findValue(MetadataPropertyUtils.relativePath);

            String extendPropertyStr = (extendPropertyNode == null || extendPropertyNode.toString().equals("null")) ? "\"\"" : extendPropertyNode.toString();
            String metadataStr = String.format("{\"%s\":%s,\"%s\":%s,\"%s\":null,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":null}",
                    MetadataPropertyUtils.header, headerStr, MetadataPropertyUtils.refs, constraintsStr, MetadataPropertyUtils.content, MetadataPropertyUtils.extendProperty,
                    extendPropertyStr, MetadataPropertyUtils.extended, extendedNode, MetadataPropertyUtils.properties, properties, MetadataPropertyUtils.version, version,
                    MetadataPropertyUtils.previousVersion, previousVersion, MetadataPropertyUtils.relativePath, relativePath, MetadataPropertyUtils.extendRule);
            metadata = this.mapper.readValue(metadataStr, GspMetadata.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return metadata;
    }

    public static ObjectMapper getMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
        mapper.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
        mapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
        mapper.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, true);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);//大小写脱敏 默认为false  需要改为tru
        return mapper;
    }

    private static String handleJsonString(String contentJson) {
        if (contentJson.startsWith("\"{\\\"")) {
            contentJson = contentJson.replace("\\\"", "\"");
            while (contentJson.startsWith("\"")) {
                contentJson = contentJson.substring(1, contentJson.length() - 1);
            }
        }
        return contentJson;
    }

    @Override
    public String serialize(Object o) {
        GspMetadata metadata = (GspMetadata) o;
        MetadataContentSerializer manager = MetadataSerializerHelper.getInstance().getManager(metadata.getHeader().getType());
        if (manager == null) {
            throw new RuntimeException("未能正常获取" + metadata.getHeader().getType() + "的序列化器，请检查配置");
        }
        JsonNode jsonNode = manager.Serialize(metadata.getContent());
        JsonNode extRuleNode = null;
        if (metadata.getExtendRule() != null) {
            var mdExtRulemanager = MdExtRuleSerializerHelper.getInstance().getManager(metadata.getHeader().getType());
            if (mdExtRulemanager != null) {
                extRuleNode = mdExtRulemanager.serialize(metadata.getExtendRule());
            }
        }

        ObjectMapper objectMapper = ServiceUtils.getMapper();
        GspMetadata newMetadata = (GspMetadata) metadata.clone();
        String metadataStr;
        try {
            metadataStr = objectMapper.writeValueAsString(newMetadata);
            JsonNode metadataObj = objectMapper.readTree(metadataStr);
            ObjectNode objNode = (ObjectNode) metadataObj;
            objNode.set(MetadataPropertyUtils.content, jsonNode);
            objNode.set(MetadataPropertyUtils.extendRule, extRuleNode);
            metadataStr = objectMapper.writeValueAsString(objNode);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("序列化元数据失败：" + metadata.getHeader().getId(), e);
        }
        return metadataStr;
    }
}
